# 拼音模块，处理关于拼音的一切问题
import re
from base import *
from pyhocon import ConfigFactory

_conf_loaded = False
_han_yu_pin_yin = []
_pin_yin_normalize = {}
_bi_pinyin = {}
_single_phonetic = {}  # 单字的注音字典


def _load_conf():
    global _conf_loaded
    global _han_yu_pin_yin
    global _pin_yin_normalize
    if _conf_loaded:
        return
    config = ConfigFactory.parse_file('pinyin.conf')
    _han_yu_pin_yin = config.get('han-yu-pin-yin')
    _pin_yin_normalize = config.get('pin-yin-normalize')
    for key in config:
        if key not in ['han-yu-pin-yin', 'pin-yin-normalize']:
            _setup_bi_pinyin(key, config.get(key))
    _conf_loaded = True


def _setup_bi_pinyin(name, config):
    global _bi_pinyin
    sheng_mu = config.get('sheng-mu')
    yun_mu = config.get('yun-mu')
    yin_jie = config.get('yin-jie')
    mapping = {}
    for pinyin in _han_yu_pin_yin:
        if pinyin in yin_jie:
            mapping[pinyin] = yin_jie[pinyin]
            continue

        if re.match('^[aoe][a-z]*', pinyin):  # 零声母音节
            if pinyin not in mapping:
                mapping[pinyin] = yun_mu[pinyin]
            continue

        s = pinyin[0:2] if re.match('^[zcs]h.+', pinyin) else pinyin[0:1]
        y = pinyin[len(s):]
        if s in sheng_mu and y in yun_mu:
            mapping[pinyin] = sheng_mu[s] + yun_mu[y]
        else:
            print('mapping is missing on: {}'.format(pinyin))
    _bi_pinyin[name] = mapping


def normalize(pinyin, fallback='XX'):
    """
    规范化拼音编码
    :param pinyin: 原拼音编码
    :param fallback: 规范化失败时回退的编码
    :return: 规范化后的拼音编码
    """
    _load_conf()
    if pinyin in _han_yu_pin_yin:
        return pinyin
    return _pin_yin_normalize.get(pinyin) if pinyin in _pin_yin_normalize else fallback


def to_bi_pinyin(schema, pinyin, fallback='XX'):
    """
    将全拼编码转换为双拼编码
    :param schema: 双拼方案
    :param pinyin: 全拼
    :param fallback: 转换失败时回退的编码
    :return: 双拼编码
    """
    _load_conf()
    if schema not in _bi_pinyin:
        return fallback

    mapping = _bi_pinyin[schema]
    if pinyin not in mapping:
        return fallback

    return mapping.get(pinyin)


def phonetic(text):
    """
    将汉字或词组注音
    :param text: 单字或词组
    :return: 拼音列表
    """
    global _single_phonetic
    if len(_single_phonetic) == 0:
        unicodePinyin = open_file_as_utf8('../data/dict_single_pinyin_merge.txt')
        for line in unicodePinyin:
            if line.startswith('#'):
                continue
            segments = line.split('\t', maxsplit=2)
            key = segments[0].strip()
            pinyins = segments[1].strip().split(',')
            _single_phonetic[key] = pinyins
        unicodePinyin.close()
    if len(text) == 1:
        return _single_phonetic.get(text) if text in _single_phonetic else 'XX'
    rtn = []
    polyphonic = False  # 是否包含多音字
    for each in text:
        if each in _single_phonetic:
            pinyins = _single_phonetic.get(each)
            if not polyphonic and len(pinyins) > 1:
                polyphonic = True
            rtn.append(pinyins[0])
        else:
            rtn.append('XX')
    if polyphonic:
        rtn[0] = '*' + rtn[0]
    return rtn


class Converter(object):
    """
    汉语拼音 -> 双拼 的转换器
    """

    def __init__(self, schema, delimiter=None) -> None:
        super().__init__()
        self.schema = schema
        self.delimiter = delimiter or ''

    def to_bi_pinyin(self, pinyins: list, do_normalize=True) -> str:
        code = ''
        try:
            if do_normalize:
                for pinyin in pinyins:
                    code += (self.delimiter + to_bi_pinyin(self.schema, normalize(pinyin, None), None))
            else:
                for pinyin in pinyins:
                    code += (self.delimiter + to_bi_pinyin(self.schema, pinyin, None))
        except:
            print('Skip pinyins: {}'.format(pinyins))
            return None
        return code[1:] if self.delimiter else code


class Encoder(Converter):
    """
    默认的编码器，将汉语拼音编码(默认的双拼方案)为定长的编码
    """

    def __init__(self, config='encode') -> None:
        super().__init__(config, ' ')

    def encode(self, pinyin) -> str:
        """
        将汉语拼音编码
        :param pinyin: 汉语拼音
        :return: 编码后的结果
        """
        if isinstance(pinyin, list):
            return self.to_bi_pinyin(pinyin)
        else:
            return self.to_bi_pinyin([pinyin])
